package ar.uba.fi.tp2Unit.assertions;

import java.lang.reflect.Array;

import ar.uba.fi.tp2Unit.framework.TestCase;

/**
 * Provides a set a assertion methods to be used in a {@link TestCase}.
 * For each assertion that fails, it throws an {@link AssertionFailException}.
 * 
 * @author grupo12
 * @see {@link TestCase}, {@link AssertionFailException}
 */
public class Assertion {
	
	private static final String FAILURE_MESSAGE = "Expected <%s> but was <%s>.";
	private static final String FAILURE_MESSAGE_DIFFERENT_LENGHT = "Expected ArrayLenght <%s> but was <%s>.";
	private static final String FAILURE_MESSAGE_NOT_SAME = "Expected not same <%s (%s)>.";
	private static final String FAILURE_MESSAGE_SAME = "Expected <%s (%s)> but was <%s (%s)>.";
	private static final String FAILURE_MESSAGE_NOT_NULL = "Expected not null.";
	private static final String FAILURE_MESSAGE_NULL = "Expected null but was <%s>.";

	public static void fail() {
		fail(null);	
	}

	public static void fail(String message) {
		throw new AssertionFailException(message);		
	}
	
	/**START assertEquals**/
	public static void assertEquals(Object expected, Object actual) {
		assertEquals(null, expected, actual);
	}

	public static void assertEquals(String message, Object expected, Object actual) {
		if (expected == null && actual == null) {
			return;
		}
		if (expected == null || actual == null || !expected.equals(actual)) {
			fail(getFormattedMessage(message, expected,actual));
		}
	}

	public static void assertEquals(double expected, double actual) {
		assertEquals(null, expected, actual);
	}

	public static void assertEquals(String message, double expected, double actual, double delta) {
		if (Double.compare(actual, expected) != 0 && !(Math.abs(expected - actual) <= delta)) {
			fail(getFormattedMessage(message, expected, actual));
		}
	}
	
	public static void assertEquals(double expected, double actual, double delta) {
		assertEquals(null, expected, actual, delta);
	}
		
	public static void assertEquals(String message, long expected, long actual) {
		assertEquals(message, (Long)expected, (Long)actual);
	}
	
	public static void assertEquals(long expected, long actual) {
		assertEquals(null, expected, actual);
	}
	/**END assertEquals**/
	
	/**START assertArrayEquals**/
	private static Object[] toObjectArray(Object array) {
	    int length = Array.getLength(array);
	    Object[] result = new Object[length];
	    for	(int i = 0; i < length; i++) {
			result[i] = Array.get(array, i);
	    }
	    return result;
	}
	
	public static void assertArrayEquals(String message, Object[] expecteds, Object[] actuals) {
		if (expecteds.length != actuals.length) {
			fail(getFormattedMessageDifferentLenght(message, expecteds.length, actuals.length));
		}
		for (int i = 0; i < expecteds.length; i++) {
			assertEquals(expecteds[i], actuals[i]);
        }
	}
	
	public static void assertArrayEquals(Object[] expecteds, Object[] actuals) {
		assertArrayEquals(null, expecteds, actuals);
	}

	public static void assertArrayEquals(String message, float[] expecteds, float[] actuals, float delta) {
		if (expecteds.length != actuals.length) {
			fail(getFormattedMessageDifferentLenght(message, expecteds.length, actuals.length));
		}
		for (int i = 0; i < expecteds.length; i++) {
			assertEquals(expecteds[i], actuals[i], delta);
        }
	}
	
	public static void assertArrayEquals(double[] expecteds, double[] actuals, double delta) {
		assertArrayEquals(null, expecteds, actuals, delta);
	}
	
	public static void assertArrayEquals(String message, double[] expecteds, double[] actuals, double delta) {
		if (expecteds.length != actuals.length) {
			fail(getFormattedMessageDifferentLenght(message, expecteds.length, actuals.length));
		}
		for (int i = 0; i < expecteds.length; i++) {
			assertEquals(message, expecteds[i], actuals[i], delta);
        }
	}
	
	public static void assertArrayEquals(float[] expecteds, float[] actuals, float delta) {
		assertArrayEquals(null, expecteds, actuals, delta);
	}
	
	public static void assertArrayEquals(String message, int[] expecteds, int[] actuals) {
		assertArrayEquals(message, toObjectArray(expecteds), toObjectArray(actuals));
	}
	
	public static void assertArrayEquals(int[] expecteds, int[] actuals) {
		assertArrayEquals(null, expecteds, actuals);
	}
	
	public static void assertArrayEquals(String message, long[] expecteds, long[] actuals) {
		assertArrayEquals(message, toObjectArray(expecteds), toObjectArray(actuals));
	}
	
	public static void assertArrayEquals(long[] expecteds, long[] actuals) {
		assertArrayEquals(null, expecteds, actuals);
	}
	
	public static void assertArrayEquals(String message, char[] expecteds, char[] actuals) {
		assertArrayEquals(message, toObjectArray(expecteds), toObjectArray(actuals));
	}
	
	public static void assertArrayEquals(char[] expecteds, char[] actuals) {
		assertArrayEquals(null, expecteds, actuals);
	}
	/**END assertArrayEquals**/
	
	public static void assertTrue(String message, boolean condition) {
		if (!condition){
			fail(getFormattedMessage(message, !condition, condition));
		}
	}
	
	public static void assertTrue(boolean condition) {
		assertTrue(null, condition);
	}
	
	public static void assertFalse(String message, boolean condition) {
		if (condition){
			fail(getFormattedMessage(message, !condition, condition));
		}
	}
	
	public static void assertFalse(boolean condition) {
		assertFalse(null, condition);
	}

	public static void assertNotNull(String message, Object object) {
		if (object == null) {
			fail(getFormattedMessageExpectedNotNull(message));
		}
	}
	
	public static void assertNotNull(Object object) {
		assertNotNull(null, object);
	}
	
	public static void assertNull(String message, Object object) {
		if (object != null) {
			fail(getFormattedMessageExpectedNull(message, object));
		}
	}

	public static void assertNull(Object object) {
		assertNull(null, object);
	}
	
	public static void assertSame(String message, Object expected, Object actual) {
		if (expected != actual) {
			fail(getFormattedMessageExpectedSame(message, expected, actual));
		}
	}
	
	public static void assertSame(Object expected, Object actual) {
		assertSame(null, expected, actual);
	}
	
	public static void assertNotSame(String message, Object unexpected, Object actual) {
		if (unexpected == actual) {
			fail(getFormattedMessageExpectedNotSame(message, unexpected));
		}
	}
	
	public static void assertNotSame(Object unexpected, Object actual) {
		assertNotSame(null, unexpected, actual);
	}
	
	private static String getFormattedMessage(String message, Object expected, Object actual) {
		String formattedMessage = String.format(FAILURE_MESSAGE, expected, actual);
		formattedMessage = (message==null) ? formattedMessage : formattedMessage + " " + message;
		return formattedMessage;
	}
	
	private static String getFormattedMessageDifferentLenght(String message, int expectedLenght, int actualLenght) {
		String formattedMessage = String.format(FAILURE_MESSAGE_DIFFERENT_LENGHT, expectedLenght, actualLenght);
		formattedMessage = (message==null) ? formattedMessage : formattedMessage + " " + message;
		return formattedMessage;
	}
	
	private static String getFormattedMessageExpectedNotSame(String message, Object same) {
		String formattedMessage = String.format(FAILURE_MESSAGE_NOT_SAME, same, same.hashCode());
		formattedMessage = (message==null) ? formattedMessage : formattedMessage + " " + message;
		return formattedMessage;
	}
	
	private static String getFormattedMessageExpectedSame(String message, Object expected, Object actual) {
		String formattedMessage = String.format(FAILURE_MESSAGE_SAME, expected, expected.hashCode(), actual, actual.hashCode());
		formattedMessage = (message==null) ? formattedMessage : formattedMessage + " " + message;
		return formattedMessage;
	}
	
	private static String getFormattedMessageExpectedNotNull(String message) {
		String formattedMessage = String.format(FAILURE_MESSAGE_NOT_NULL);
		formattedMessage = (message==null) ? formattedMessage : formattedMessage + " " + message;
		return formattedMessage;
	}

	private static String getFormattedMessageExpectedNull(String message, Object notNull) {
		String formattedMessage = String.format(FAILURE_MESSAGE_NULL, notNull);
		formattedMessage = (message==null) ? formattedMessage : formattedMessage + " " + message;
		return formattedMessage;
	}
}
